import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Console;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.regex.*;

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;

public class Wikigolfer {
	
	private static BrowserVersion BROWSER = BrowserVersion.FIREFOX_3;
	private static String WIKI_TEXTAREA_ID = "wpTextbox1";
	private static String WIKI_LINK_REGEX = "\\[\\[[\\w\\d\\s|\\-\\(\\)]*\\]\\]";
	private static String WIKI_LINK_STRUCT = "http://en.wikipedia.org/w/index.php?title=%s&action=edit";
	private static String CACHE_PATH = "data/%s.txt";
	
	private static int MAX_STROKES = 3;
	
	private boolean printErrors = true;
	private boolean printProgress = true;
	private boolean dieOnError = false;
	
	private long start;

	public static void main (String[] args) {
		Wikigolfer wiki = new Wikigolfer();
		wiki.golf("Post_Office_Protocol", "QWERTY");
	}
	
	public void golf(String start_pagename, String goal_pagename) {
		this.start = System.currentTimeMillis();
		
		ArrayList<String> hist = new ArrayList<String>();
		hist.add(start_pagename);
		this.golf(start_pagename, goal_pagename, 0, hist);
		
	}
	
	public void golf(String current, String goal, int strokes, ArrayList<String> hist) {
		
		String[] links = this.getWikiLinks(current);
		
		if (links != null) {
			try {
				for (String link : links) {
					if (link.equalsIgnoreCase(goal)) {
						// We found it!
						System.out.println("***** SUCCESS *****");
						hist.add(link);
						this.printHistory(hist);
						System.out.println("Search took: "+(( System.currentTimeMillis() - this.start)/1000)+" seconds");
						System.exit(1);
					}
					else if (strokes < MAX_STROKES) {
						strokes++;
						hist.add(link);
						this.golf(link, goal, strokes, hist);
						strokes--;
					}
					else {
						// We've exceed the maximum number of allowed strokes
						if (this.printProgress) {
							System.out.println("Maximum strokes ("+Wikigolfer.MAX_STROKES+") reached");
							this.printHistory(hist);
						}
						hist.remove(hist.size()-1);
						strokes--;
						//System.exit(1);
						return;
					}
				}
				
				return;
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
			}
		}
		else {
			hist.remove(hist.size()-1);
		}
	}
	
	public String[] getWikiLinks (String page) {
		
		File f = new File(String.format(Wikigolfer.CACHE_PATH, page));
		if (f.exists()) {
			// If the file exists, read the links from there
			String[] ret = new String[0];
			
			try {
				BufferedReader in = new BufferedReader(new FileReader(String.format(Wikigolfer.CACHE_PATH, page)));
				String line = in.readLine();
				
				while (line != null) {
					ret = this.extendStringArray(ret, 1);
					ret[ret.length-1] = line;
					line = in.readLine();
				}
			} 
			catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			return ret;
		}
		else {
			String url = String.format(Wikigolfer.WIKI_LINK_STRUCT, page);
			
			final WebClient webClient = new WebClient(Wikigolfer.BROWSER);
			webClient.setCssEnabled(false);
			webClient.setJavaScriptEnabled(false);
			
			String wikiText = "";
			
			try {
				final HtmlPage p = webClient.getPage(url);
				final HtmlTextArea textarea = p.getHtmlElementById(Wikigolfer.WIKI_TEXTAREA_ID);
				wikiText = textarea.getText();
			}
			catch (Exception e) {
			}
			
			webClient.closeAllWindows();
		
			Pattern pattern = Pattern.compile(WIKI_LINK_REGEX);
			Matcher matcher = pattern.matcher(wikiText);
			String[] ret = new String[0];
				
			boolean found = false;
		    while (matcher.find()) {
		    	ret = (String[]) extendStringArray(ret, 1);
		        ret[ret.length-1] = matcher.group();
		        found = true;
		    }
		    if (!found) {
		    	this.logError("NO [[WIKI LINKS]] FOUND IN : "+wikiText);
		        return null;
		    }
		    else {
		        // Remove the [[ and ]]
		        for (int i = 0; i < ret.length; i++) {
		        	ret[i] = ret[i].replaceAll("\\[", "");
		        	ret[i] = ret[i].replaceAll("\\]", "");
		        	
		        	// If a wiki link contains a pipe |, such as in renaming a link, remove the rename.
		        	if (ret[i].contains("|")) {
		        		// Remove everything after the pipe
		        		ret[i] = ret[i].substring(0, ret[i].indexOf('|'));
		        	}
		        	// If a wiki link contains a pipe #, such as in renaming anchor, remove the anchor.
		        	if (ret[i].contains("|")) {
		        		// Remove everything after the pipe
		        		ret[i] = ret[i].substring(0, ret[i].indexOf('#'));
		        	}
		        	
		        	// Replace spaces with underscores
		        	ret[i] = ret[i].replaceAll(" ", "_");
		        }
		        
			    if (wikiText.contains("#REDIRECT")) {
			    	return this.getWikiLinks(ret[0]);
			    }
			    else {
					this.logLinks(page, ret);
		            return ret;
			    }
			}
		}
	}
	
	private void logError(String err) {
		System.err.println("\n***** ERROR *****");
		System.err.println(err);
		if (this.dieOnError) System.exit(1);
	}
	
	private String[] extendStringArray(String[] arr, int len) {
		String[] temp = new String[arr.length+len];
		System.arraycopy(arr, 0, temp, 0, arr.length);
		return temp;
		
	}
	
	private void printHistory (ArrayList<String> hist) {
		for (int i = 0 ; i < hist.size(); i++) {
			
			System.out.print("("+i+") "+hist.get(i));
			
			if (i != hist.size()-1)
				System.out.print(" -> ");
		}
		
		System.out.print("\n");
	}
	
	private void logLinks (String page, String[] links) {
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(String.format(Wikigolfer.CACHE_PATH, page)));
			String data = "";
			for (String d : links) {
				data += d + "\n";
			}
			out.write(data);
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}
